package org.jenkinsci.plugins.pipeline.modeldefinition;

import groovy.lang.Binding;
import groovy.lang.Script;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.jenkinsci.plugins.pipeline.modeldefinition.parser.RuntimeASTTransformer;
import org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.Whitelisted;
import org.jenkinsci.plugins.workflow.cps.CpsScript;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;

/**
 * Base class for classes generated by {@link RuntimeASTTransformer.Wrapper} when code splitting transform is enabled.
 * This class is as close a possible to a transparent pass-through to CpsScript.
 * However, this class does not get the special whitelisting granted to CpsScript instances, so
 * care should be be taken regarding what code is added to these classes' methods.
 *
 * @author Liam Newman
 */
public class RuntimeContainerBase extends Script implements Serializable {

    // The parent script instance.
    // On restart this is serialized and deserialized.
    @NonNull
    private final CpsScript workflowScript;

    @Whitelisted
    protected RuntimeContainerBase(@NonNull CpsScript workflowScript) {
        super();
        this.workflowScript = workflowScript;
    }

    @Override
    public Object run() {
        throw new AssertionError("This is a helper script class. It cannot be run.");
    }

    @Override
    public Binding getBinding() {
        return workflowScript.getBinding();
    }

    @Override
    public void setBinding(Binding binding) {
        throw new AssertionError("The binding of this class is ignored. Setting it makes no sense.");
    }

    @Override
    public Object getProperty(String propertyName) {
        return workflowScript.getProperty(propertyName);
    }

    @Override
    public void setProperty(String propertyName, Object newValue) {
        workflowScript.setProperty(propertyName, newValue);
    }

    @Override
    public Object evaluate(String script) throws CompilationFailedException {
        return workflowScript.evaluate(script);
    }

    @Override
    public Object evaluate(File file) throws CompilationFailedException, IOException {
        return workflowScript.evaluate(file);
    }

    @Override
    public void run(File file, String[] arguments) throws CompilationFailedException, IOException {
        workflowScript.run(file, arguments);
    }

    /**
     * @see CpsScript#sleep(long)
     */
    @Whitelisted
    public Object sleep(long arg) {
        return InvokerHelper.invokeMethod(workflowScript, "sleep", arg);
    }

    /* Overriding methods defined in DefaultGroovyMethods
            if we don't do this, definitions in DefaultGroovyMethods get called. One problem
            is that most of them are not whitelisted, and the other problem is that we want them to
            be forwarded to the parent script.

            In CpsScript we override these methods and redefine them as variants of the 'echo' step,
            so for this to work the same from this script container class, we need to redefine them.
     */
    @Whitelisted
    public void println(Object arg) {
        InvokerHelper.invokeMethod(workflowScript, "println", new Object[] {arg});
    }

    @Whitelisted
    public void println() {
        InvokerHelper.invokeMethod(workflowScript, "println", new Object[0]);
    }

    @Whitelisted
    public void print(Object arg) {
        InvokerHelper.invokeMethod(workflowScript, "print", new Object[] {arg});
    }

    @Whitelisted
    public void printf(String format, Object value) {
        InvokerHelper.invokeMethod(workflowScript, "printf", new Object[] {format, value});

    }
}
